/*
 @Name: rayui
 @Author: ray
 @License: MIT
*/
(function (win, doc, $, undef) {
    "use strict";

    //防止多次加载
    if (window.rayui && rayui.define) return;

    var getlastScript = function () {
        var js = doc.scripts, last = js.length - 1;
        return doc.scripts[last];
    },
        getCurScript = function () {
            if (doc.currentScript) return doc.currentScript;
            return getlastScript();
        },
        $doc = $(doc),
        needWait = false,
        exportCompleted = false,
        currentScript = getCurScript(),
        modules = {
            code: "modules/code", //code
            color: "modules/color", //color
            colorutils: "modules/colorutils", //colorutils
            calender: "modules/calender", //calender
            checkbox: "modules/checkbox", //checkbox
            elem: "modules/elem", //elem
            form: "modules/form", //form
            jqlibs: "modules/jqlibs", //jqlibs
            layer: "modules/layer", //弹层
            lazy: "modules/lazy", //懒加载
            loadingbtn: "modules/loadingbtn", //loadingbtn
            navbar: "modules/navbar", //navbar
            page: "modules/page", //page
            pagescroll: "modules/pagescroll", //pagescroll
            progress: "modules/progress", //progress
            radiobtn: "modules/radiobtn", //radiobtn
            ringbtn: "modules/ringbtn", //ringbtn
            switchbtn: "modules/switchbtn", //switchbtn
            select: "modules/select", //select
            selecttree: "modules/selecttree", //selecttree
            tab: "modules/tab", //tab
            table: "modules/table", //表格
            timerbtn: "modules/timerbtn", //timerbtn
            template: "modules/template", //模板引擎
            tree: "modules/tree", //tree
            treegrid: "modules/treegrid", //treegrid
            validate: "modules/validate" //validate
        },        
        Rayui = function () {
            var that = this;
            that.$ = $;
            //版本号
            that.version = '1.5.0';
            //rayui根目录，所有目录最后都不包含/
            that.rayuiJsPath = function () {
                var src = currentScript.src;
                return src.substring(0, src.lastIndexOf('/'));
            }();
            that.browser = function () {
                var ua = navigator.userAgent.toLowerCase();
                var result, index, data = [];

                var gps = ua.match(/(firefox|metasr|maxthon|opera|qqbrowser|bidubrowser|ubrowser|msie|edge|chrome|safari(?=[\/\s]))[\/\s]*(\d+)/g) || [];
                $.each(gps, function (a, s) {
                    var p = s.split(/[\/\s]/);
                    data.push({
                        name: p[0],
                        version: p[1]
                    });
                });
                if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("firefox") >= 0 })) !== -1) {//火狐
                    result = data[index];
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("metasr") >= 0 })) !== -1) {//搜狗
                    result = data[index];
                    result.name = "sougou";
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("maxthon") >= 0 })) !== -1) {//遨游
                    result = data[index];
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("opera") >= 0 })) !== -1) {//欧朋
                    result = data[index];
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("qqbrowser") >= 0 })) !== -1) {//QQ
                    result = data[index];
                    result.name = "qq";
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("bidubrowser") >= 0 })) !== -1) {//百度
                    result = data[index];
                    result.name = "baidu";
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("ubrowser") >= 0 })) !== -1) {//UC
                    result = data[index];
                    result.name = "uc";
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("msie") >= 0 })) !== -1) {//IE
                    result = data[index];
                    result.name = "ie";
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("edge") >= 0 })) !== -1) {//Edge
                    result = data[index];
                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("chrome") >= 0 })) !== -1) {//谷歌，注意区分360
                    result = data[index];
                    var is360 = function (option, value) {
                        var mimeTypes = navigator.mimeTypes;
                        for (var mt in mimeTypes) {
                            if (mimeTypes[mt][option] == value) {
                                return true;
                            }
                        }
                        return false;
                    }("type", "application/vnd.chromium.remoting-viewer");

                    if (is360) {
                        result.name = '360';//360
                    }

                } else if ((index = data.rayIndexOf("name", function (v) { return v.indexOf("safari") >= 0 })) !== -1) {//safari

                    result = data[index];
                    result.version = ua.match(/version\/(\d+)/)[1];
                }

                return result;
            }();

            that.basePath = win.location.origin || win.location.protocol + "//" + win.location.host;

            //兼容性：IE10+,Firefox10+,Chrome14+,Opera12.1+,Safari7.1+
            var hiddenProperty = 'hidden' in doc
                ? 'hidden'
                : 'webkitHidden' in doc
                    ? 'webkitHidden'
                    : 'mozHidden' in doc
                        ? 'mozHidden' : null;
            //IE8下有问题
            if (hiddenProperty === null) {
                that.hidden = true;//这里有问题
            } else {
                var visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');
                doc.addEventListener(visibilityChangeEvent,
                    function () {
                        that.hidden = doc[hiddenProperty];
                    });
                that.hidden = doc[hiddenProperty];
            }
        },
        loadingMods = [],
        exportModules = {},
        config = {
            moduleVersion: {
                //指定刷新前端组件js，例如：
                //table: "1.7"
            }
        };

    //日期+时间
    var regOnlyTime = /^(20|21|22|23|(|[01])\d):(|[0-5])\d(|:(|[0-5])\d)$/;
    var regDateTime = /^[1-9]\d{3}\/((|0)[1-9]|1[0-2])\/((|0)[1-9]|[1-2][0-9]|3[0-1])(|\s(20|21|22|23|(|[01])\d):(|[0-5])\d(|:(|[0-5])\d))$/;

    /**
     * 参考https://www.cnblogs.com/sky6699/p/7206383.html
     * 对Date的扩展，将 Date 转化为指定格式的String
     * 年(y)可以用 1-4 个占位符，
     * 月(M)、日(d)、小时(H)、分(m)、秒(s)可以用 1-2 个占位符，
     * 毫秒(f)可以用 1-3 个占位符
     * 例子：
     * (new Date()).Format("yyyy-MM-dd hh:mm:ss.fff") ==> 2006-07-02 08:09:04.423
     * (new Date()).Format("yyyy-M-d h:m:s.ff")      ==> 2006-7-2 8:9:4.18
     * @param {any} format yyyy-MM-dd hh:mm:ss
     */
    Date.prototype.format = function (format) {

        var o =
        {
            "M+": this.getMonth() + 1, //month
            "d+": this.getDate(),    //day  d
            "H+": this.getHours(),   //hour
            "m+": this.getMinutes(), //minute
            "s+": this.getSeconds() //second
        }
        if (/(y+)/.test(format))
            format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
        for (var k in o)
            if (new RegExp("(" + k + ")").test(format))
                format = format.replace(RegExp.$1, o[k] < 10 ? "0"+o[k] : o[k]);
        if (/(f+)/.test(format))
            format = format.replace(RegExp.$1, (this.getMilliseconds() + "").substr(0, RegExp.$1.length));
        return format;
    }

    Array.prototype.raysort = function (key, desc) {
        if (!key) return this;
        this.sort(function (o1, o2) {
            var v1 = o1[key], v2 = o2[key];
            //把空值放在前面
            if (v1 && !v2) {
                return 1;
            } else if (!v1 && v2) {
                return -1;
            }

            //类型不一致
            var type_v1 = typeof v1, type_v2 = typeof v2;
            if (type_v1 !== type_v2) {
                return v1 > v2 ? 1 : -1;
            }

            //return>0，交换位置
            //数字
            if (type_v1 === "number")
                return v1 - v2;

            //字符串数字
            if (!isNaN(v1) && !isNaN(v2))
                return parseFloat(v1) - parseFloat(v2);

            //时间
            var tmpV1 = v1.replace(/-/g, '/'), tmpV2 = v2.replace(/-/g, '/');
            var isdate_v1 = regDateTime.test(tmpV1) || regOnlyTime.test(tmpV1);
            var isdate_v2 = regDateTime.test(tmpV2) || regOnlyTime.test(tmpV2);
            if (isdate_v1 && isdate_v2) {
                return new Date(tmpV1) - new Date(tmpV2);
            }

            //字符
            return v1 > v2 ? 1 : -1;
        });

        desc && this.reverse(); //倒序
        return this;
    }

    /**
     * model数组
     * @param {any} key 属性
     * @param {any} val 可以是数值|function(value)，函数返回true|false
     * @param {any} startIndex
     */
    Array.prototype.rayIndexOf = function (key, val, startIndex) {
        startIndex = startIndex || 0;
        var length = this.length - 1,isFuc=typeof val === "function";
        while (length >= startIndex) {
            var flag = isFuc ? val(this[length][key]) : this[length][key] === val;
            if (flag)
                return length;
            length--;
        }
        return -1;
    }


    //IE8没有indexOf函数
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (elt) {
            var len = this.length >>> 0;

            var from = Number(arguments[1]) || 0;
            from = (from < 0)
                ? Math.ceil(from)
                : Math.floor(from);
            if (from < 0)
                from += len;

            for (; from < len; from++) {
                if (from in this && this[from] === elt)
                    return from;
            }
            return -1;
        };
    }

    Rayui.prototype.log = function (msg) {
        typeof console === 'object' && (console.log(msg));
    }

    Rayui.prototype.error = function (msg) {
        typeof console === 'object' && (console.error(msg));
    }

    Rayui.prototype.codeTime = function (msg, func) {
        var time1 = new Date().getTime();
        func();
        var time2 = new Date().getTime();
        var str = msg + ",耗时：" + (time2 - time1) + "毫秒";
        this.log(str);
    }

    Rayui.prototype.isInclude = function (name) {
        var js = /js$/i.test(name);
        var es = document.getElementsByTagName(js ? 'script' : 'link');
        for (var i = 0; i < es.length; i++)
            if (es[i][js ? 'src' : 'href'].indexOf(name) !== -1) return true;
        return false;
    }

    Rayui.prototype.loadScript = function (url, onsuccess, onerror) {
        var options = typeof url === "string" ? { url: url } : url;
        typeof options.async !== "boolean" && (options.async = true);
        var script;
        if (options.async) {
            script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = options.url;
            var head = document.head || document.getElementsByTagName('head')[0];
            head.appendChild(script);
        } else {
            document.write('<script src="' + options.url + '"><\/script>');
            script = getlastScript();
        }
        if (typeof onerror === "function") {
            script.onerror = function () {
                this.options = options;
                onerror.call(this);
            };
        }
        if (typeof onsuccess !== "function") return;
        if (script.readyState) {//IE
            script.onreadystatechange = function () {
                if (script.readyState === 'complete' || script.readyState === 'loaded') {
                    script.onreadystatechange = null;
                    this.options = options;
                    onsuccess.call(this);
                }
            }
        } else {//非IE
            script.onload = function () {
                this.options = options;
                onsuccess.call(this);
            }
        }
    }

    Rayui.prototype.loadCss = function (url, onsuccess, onerror) {
        var options = typeof url === "string" ? { url: url } : url;
        var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = "stylesheet";
        link.href = options.url;
        var head = document.head || document.getElementsByTagName('head')[0];
        head.appendChild(link);
        if (typeof onerror === "function") {
            link.onerror = function () {
                this.options = options;
                onerror.call(this);
            };
        }
        if (typeof onsuccess !== "function") return;
        if (link.readyState) {//IE
            link.onreadystatechange = function () {
                if (link.readyState === 'complete' || link.readyState === 'loaded') {
                    link.onreadystatechange = null;
                    this.options = options;
                    onsuccess.call(this);
                }
            }
        } else {//非IE
            link.onload = function () {
                this.options = options;
                onsuccess.call(this);
            }
        }
    }
    
    Rayui.prototype.config = function (options) {
        options = options || {};
        $.extend(true, config, options);
        return this;
    };

    Rayui.prototype.bindDocEvt = function (plugin, func) {
        if (typeof func !== "function") return this;

        $doc.on("click." + plugin, func);
        Rayui.docEvts = Rayui.docEvts || {};
        Rayui.docEvts[plugin] = func;
        return this;
    };

    Rayui.prototype.toggleDocEvt = function () {
        for (var key in Rayui.docEvts)
            Rayui.docEvts[key]();
        return this;
    };

    Rayui.prototype.jsAsync = function () {
        var script = getCurScript();
        return script.async;
    }

    Rayui.prototype.define = function (uses, callback, async) {
        //uses可能为null、undef、""、["",""]
        var that = this,
            _fireCallBack = function () {
                callback.call(that, function (app, model) {
                    that[app] = model;
                    exportModules[app] = true;
                });                
            },
            fireCallBack = function () {
                if (async) {
                    $(function () {
                        _fireCallBack();
                    });
                } else {
                    _fireCallBack();
                }
            };
        //不需要依赖
        if (typeof uses === "function") {
            async = callback;
            callback = uses;
            uses = null;
        }
        if (typeof callback !== "function") return;
        
        if (uses === undef || uses == null || uses === "") {
            fireCallBack();
            return;
        }

        that.use.call(that, uses, function () {
            fireCallBack();
        }, async);
    }

    Rayui.prototype.use = function (uses, callback, async) {
        if (uses === undef || uses == null || uses === "") {
            $(function () { callback() });
            return;
        }
        uses = typeof uses === 'string' ? [uses] : uses;
        var that = this,
            script = getlastScript(),
            count = uses.length,
            timeCheck = false,
            loadSuccess = function () {
                if (timeCheck) return;
                timeCheck = true;
                var chkCount = 0, chkAllCount = 400;//20s返回(50*400=20000)
                //判断export成功再回调
                (function chkExport() {
                    if (++chkCount > chkAllCount) {
                        that.log(script.src + "加载失败");
                        return;
                    }
                    var i = count - 1;
                    while (i >= 0) {
                        var mod = uses[i--];
                        if (!exportModules[mod]) {
                            setTimeout(chkExport, 50);
                            return;
                        } else {
                            typeof rayui.onLoadModule === "function" && rayui.onLoadModule(mod);
                        }
                    }
                    //保证dom加载完成
                    $(function() { callback() });
                })();
            };

        //循环加载uses
        $.each(uses, function (a, mod) {
            if (mod === undef || mod === "") return;
            var modPath = modules[mod];
            //验证加载完成
            if (exportModules[mod]) {
                loadSuccess();
                return;
            }

            //避免重复加载
            var lastName = mod.substring(mod.lastIndexOf("/") + 1),
                index = loadingMods.indexOf(lastName);
            if (index > -1) {
                //这里不一定是加载成功，有可能是重复加载导致的return
                loadSuccess();
                return;
            }
            loadingMods.push(lastName);

            //如果是rayui模块则按rayui拼接路径
            //如果以"/"开头，则为根目录+虚拟目录+数值
            //其他相对路径直接拼接
            var virtualPath = config["virtualPath"] || "",
                modVersion = "" + (config["moduleVersion"][mod] || "") + (config["moduleVersion"]["all"] || ""),
                url = modPath
                    ? that.rayuiJsPath + "/" + modPath + ".js"
                    : that.basePath + virtualPath + modPath + ".js";

            modVersion && (url += "?v=" + modVersion);

            async === false && (url = { url: url, async: async });
            that.loadScript(url, loadSuccess);
        });//end each
    }

    //拓展模块
    Rayui.prototype.extend = function (options) {
        var that = this;
        //验证模块是否被占用
        options = options || {};
        for (var o in options) {
            if (modules[o]) {
                that.error('已存在模块' + o);
            } else {
                modules[o] = options[o];
            }
        }
    }

    Rayui.prototype.setCookie = function (name, value, expires, path, domain, secure) {
        var exp = new Date();
        if (expires) exp.setTime(exp.getTime() + expires * 1);
        document.cookie = name + "=" + encodeURIComponent(value)
            + (expires ? ";expires=" + exp.toGMTString() : "")
            + (";path=" + (path || "/"))
            + (domain ? ";domain=" + domain : "")
            + (secure ? ";secure" : "");
    }

    Rayui.prototype.getCookie = function (name) {
        var reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
        var value = document.cookie.match(reg);
        return null != value ? decodeURIComponent(value[2]) : null;
    }

    Rayui.prototype.delCookie = function (name, path, domain) {
        var exp = new Date();
        exp.setTime(exp.getTime() - 1000);
        var cval = this.getCookie(name);
        if (cval != null)
            document.cookie = name + "=" + cval
                + (";expires=" + exp.toGMTString())
                + (";path=" + (path || "/"))
                + (domain ? ";domain=" + domain : "");
    }

    Rayui.prototype.ready = function (callback) {
        if (needWait) {
            return (function chk() {
                if (!exportCompleted)
                    return setTimeout(chk, 50);
                return callback();
            })();
        }

        return $(function () { callback() });
    }

    /**
     * 产生随机数
     * @param {any} min
     * @param {any} max
     * @param {any} decimal 小数位数
     */
    Rayui.prototype.random = function (min, max, decimal) {
        decimal = decimal || 0;
        if (decimal < 0) decimal = 0;
        if (min > max) return 0;
        var rand = Math.random(); //0.0~1.0，包含0.0不包含1.0
        if (rand === 0.0) return min;

        var res = rand * (max - min) + min;
        if (decimal === 0) return Math.round(res);
        var rate = Math.pow(10, decimal);
        return Math.round(res * rate) / rate;
    }

    win.rayui = $.extend(win.rayui || {}, new Rayui());

    //同步加载use
    var attrUsers = currentScript.getAttribute("use");
    if (attrUsers && attrUsers.length > 0) {
        var list = attrUsers.split(',');
        needWait = true;
        rayui.use(list, function () {
            exportCompleted = true;
        }, false);
    }
    
})(window, document, jQuery);
